home *** CD-ROM | disk | FTP | other *** search
- <?xml version="1.0"?>
-
- <bindings id="autocompleteBindings"
- xmlns="http://www.mozilla.org/xbl"
- xmlns:html="http://www.w3.org/1999/xhtml"
- xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
-
- <binding id="autocomplete" extends="chrome://global/content/xulBindings.xml#textfield">
- <content>
- <xul:box class="textfield-internal-box" flex="1">
- <html:input class="textfield-input" flex="1" inherits="onfocus,onblur,value,type,maxlength,disabled,size,readonly"/>
- <xul:popupset ignorekeys="true"
- oncommand="var me = this.parentNode.parentNode; me.privatefunc.onMenuCommand(me, this);"
- >
- <xul:popup oncreate="this.parentNode.parentNode.parentNode.menuOpen='true'" ondestroy="this.parentNode.parentNode.parentNode.menuOpen='false';">
- </xul:popup>
- </xul:popupset>
- </xul:box>
- </content>
-
- <implementation>
- <property name="value"
- onset="
- if (this.ignoreInputEventTimer)
- clearTimeout(this.ignoreInputEventTimer);
- this.ignoreInputEvent = true;
- this.ignoreInputEventTimer = setTimeout(this.privatefunc.resetInput, 250, this);
- return document.getAnonymousNodes(this)[0].firstChild.value = val;"
- onget="this.privatefunc.cleanupInputField(this); return document.getAnonymousNodes(this)[0].firstChild.value;"
- />
-
- <property name="timeout"
- onset="return this.setAttribute('timeout', val);"
- onget="return this.getAttribute('timeout');"
- />
-
- <property name="displayMenu"
- onset="return this.setAttribute('displayMenu', val);"
- onget="return this.getAttribute('displayMenu');"
- />
-
- <property name="autoCompleteSession">
- <![CDATA[
- if (this.getAttribute('searchSessionType') != "") {
- var searchSession = unescape('@mozilla%2Eorg%2FautocompleteSession%3B1%3Ftype=');
- searchSession = searchSession + this.getAttribute('searchSessionType');
- try {
- Components.classes[searchSession].getService(Components.interfaces.nsIAutoCompleteSession);
- // var session = Components.classes[searchSession].createInstance();
- // session.QueryInterface(Components.interfaces.nsIAutoCompleteSession);
- } catch (e) {dump("### ERROR, cannot create a search session. " + e + "\n");}
- }
- ]]>
- </property>
-
- <property name="disableAutocomplete"
- onset="return this.setAttribute('disableAutocomplete', val);"
- onget="return this.getAttribute('disableAutocomplete');"
- />
-
- <property name="autoCompleteTimer">
- <![CDATA[
- 0;
- ]]>
- </property>
-
- <property name="ignoreInputEventTimer">
- <![CDATA[
- 0;
- ]]>
- </property>
-
- <property name="lastResults">
- <![CDATA[
- var results = Components.classes["@mozilla.org/autocomplete/results;1"].createInstance();
- results.QueryInterface(Components.interfaces.nsIAutoCompleteResults);
- ]]>
- </property>
-
- <property name="lastKeyPressed"> <![CDATA[ 0; ]]> </property>
- <property name="noDirectMatch"> <![CDATA[ 0; ]]> </property>
- <property name="menuOpen"> <![CDATA[ 0; ]]> </property>
-
- <property name="autoCompleteListener">
- <![CDATA[
- ({
- onAutoComplete: function(result, status) {
- var me = this.param;
- if (status == Components.interfaces.nsIAutoCompleteStatus.failed)
- return;
-
- if (me.disableAutocomplete == "true")
- return;
-
- me.lastResults = result;
- if (status == Components.interfaces.nsIAutoCompleteStatus.ignored ||
- status == Components.interfaces.nsIAutoCompleteStatus.noMatch)
- return;
-
- if (result == null && result.items.Count() == 0)
- return;
-
- if (result.defaultItemIndex > result.items.Count())
- result.defaultItemIndex = 0;
-
- /* Do not alter the user input when deleting characters */
- if (me.lastKeyPressed == 8 /*vk_back*/ || me.lastKeyPressed == 46 /*vk_delete*/)
- result.defaultItemIndex = -1;
-
- var inputElement = document.getAnonymousNodes(me)[0].firstChild;
-
- //Time to build the new edit field value
- //First, check if the search string correspond to the current value of the field, else ignore it
- if (result.searchString != inputElement.value)
- return;
-
- var item = null;
- if (result.defaultItemIndex != -1)
- {
- item = result.items.QueryElementAt(result.defaultItemIndex, Components.interfaces.nsIAutoCompleteItem);
- var match = item.value.toLowerCase();
- var entry = inputElement.value.toLowerCase();
- if (entry != match)
- {
- me.ignoreInputEvent = true;
- if (match.substring(0, entry.length) == entry)
- {
- inputElement.value = inputElement.value + item.value.substring(entry.length, match.length);
- inputElement.setSelectionRange(entry.length, match.length);
- me.noDirectMatch = false;
- }
- else
- {
- inputElement.value = inputElement.value + " >> " + item.value;
- inputElement.setSelectionRange(entry.length, inputElement.value.length);
- me.noDirectMatch = true;
- }
- me.ignoreInputEvent = false;
- }
- }
-
- //Now, build the popup content
- if (me.displayMenu == "false")
- return;
-
- var popupset = document.getAnonymousNodes(me)[0].childNodes[1];
- var popupElement = popupset.firstChild;
-
- //First, remove all the current menu items
- for (i = popupElement.childNodes.length - 1; i >= 0 ; i --)
- popupElement.removeChild(popupElement.childNodes[i]);
-
- //Then build the new menu items
- for (var i = 0; i < result.items.Count(); i ++)
- {
- item = result.items.QueryElementAt(i, Components.interfaces.nsIAutoCompleteItem);
- var menuitem = document.createElement("menuitem");
- menuitem.setAttribute('data', i);
- menuitem.setAttribute('value', item.value);
- popupElement.appendChild(menuitem);
- // dump(" match=" + item.value + "\n");
- }
- // dump(" count=" + result.items.Count() + ", default=" + result.defaultItemIndex + "\n");
-
- me.privatefunc.selectedItemIndex = result.defaultItemIndex;
- if (result.defaultItemIndex != 0 || result.items.Count() != 1)
- {
- // me.privatefunc.closePopupMenu(me); //Close it first as openPopup seems to work as a toggle!
- popupset.firstChild.openPopup(document.getAnonymousNodes(me)[0].firstChild, -1, -1, "popup", "bottomleft", "topleft");
-
- if (result.defaultItemIndex != -1)
- popupElement.activeChild = popupElement.childNodes[result.defaultItemIndex];
- }
- },
-
- param: this
- })
- ]]>
- </property>
-
- <property name="privatefunc">
- <![CDATA[
- ({
-
- onMenuCommand: function(me, popupSetElem) {
- var inputElement = document.getAnonymousNodes(me)[0].firstChild;
- var popupElem = popupSetElem.firstChild;
-
- me.ignoreInputEvent = true;
-
- inputElement.value = popupElem.activeChild.getAttribute("value");
- inputElement.setSelectionRange(inputElement.value.length, inputElement.value.length);
-
- needToAutocomplete = false;
- me.privatefunc.selectedItemIndex = popupElem.activeChild.getAttribute("data");
-
- me.ignoreInputEvent = false;
-
- return;
- },
-
- callListener: function(me, action) {
- switch (action) {
- case 'startLookup':
- if (me.disableAutocomplete == "true")
- return;
- inputElement = document.getAnonymousNodes(me)[0].firstChild;
- if (!me.lastResults || inputElement.value != me.lastResults.searchString)
- me.autoCompleteSession.onStartLookup(inputElement.value, me.lastResults, me.autoCompleteListener);
- break;
-
- case 'stopLookup':
- me.autoCompleteSession.onStopLookup();
- break;
-
- case 'autoComplete':
- if (me.autoCompleteTimer) {
- clearTimeout(me.autoCompleteTimer);
- me.autoCompleteTimer = 0;
- }
- me.needToAutocomplete = false;
- if (this.disableAutocomplete == "true")
- return;
- var inputElement = document.getAnonymousNodes(me)[0].firstChild;
- me.autoCompleteSession.onAutoComplete(inputElement.value, me.lastResults, me.autoCompleteListener);
- break;
- }
- },
-
- finishAutoComplete: function(me, event) {
- me.privatefunc.closePopupMenu(me);
- if (me.disableAutocomplete == "true")
- return;
-
- var inputElement = document.getAnonymousNodes(me)[0].firstChild;
- var value = inputElement.value;
- var entry = value.substring(0, inputElement.selectionStart) + value.substring(inputElement.selectionEnd, value.length);
-
- if (me.lastResults)
- {
- if (me.lastResults.searchString == entry)
- {
- me.ignoreInputEvent = true;
- try {
- inputElement.value = me.lastResults.items.QueryElementAt(me.lastResults.defaultItemIndex, Components.interfaces.nsIAutoCompleteItem).value;
- } catch(e) {};
- inputElement.setSelectionRange(inputElement.value.length, inputElement.value.length);
- me.ignoreInputEvent = false;
- return;
- }
- }
-
- me.privatefunc.callListener(me, 'autoComplete');
- },
-
- closePopupMenu: function(me) {
- var popup = document.getAnonymousNodes(me)[0].childNodes[1].firstChild;
- if (popup && me.menuOpen == "true")
- popup.closePopup();
- },
-
- cleanupInputField: function(me) {
- if (me.noDirectMatch)
- {
- var inputElement = document.getAnonymousNodes(me)[0].firstChild;
- var value = inputElement.value;
- var index = value.indexOf(">> ");
- if (index >= 0)
- {
- me.ignoreInputEvent = true;
- inputElement.value = value.substr(index + 3);
- inputElement.setSelectionRange(inputElement.value.length, inputElement.value.length);
- me.ignoreInputEvent = false;
- }
- }
- },
-
- keyNavigation: function(me, event, popup) {
- if (me.lastResults == null)
- return;
-
- var inputElement = document.getAnonymousNodes(me)[0].firstChild;
- if (event.keyCode == 38 || event.keyCode == 40)
- {
- if (event.keyCode == 38)
- {
- if (me.privatefunc.selectedItemIndex <= -1)
- me.privatefunc.selectedItemIndex = me.lastResults.items.Count() - 1;
- else
- me.privatefunc.selectedItemIndex --;
- }
- else
- {
- me.privatefunc.selectedItemIndex ++;
- if (me.privatefunc.selectedItemIndex >= me.lastResults.items.Count())
- me.privatefunc.selectedItemIndex = -1
- }
-
- me.ignoreInputEvent = true;
- if (me.privatefunc.selectedItemIndex == -1)
- inputElement.value = me.lastResults.searchString;
- else
- inputElement.value = me.lastResults.items.QueryElementAt(me.privatefunc.selectedItemIndex, Components.interfaces.nsIAutoCompleteItem).value;
- inputElement.setSelectionRange(inputElement.value.length, inputElement.value.length);
- noDirectMatch = false;
- needToAutocomplete = false;
- me.ignoreInputEvent = false;
-
- if (popup)
- {
- if (me.privatefunc.selectedItemIndex >= 0)
- popup.activeChild = popup.childNodes[me.privatefunc.selectedItemIndex];
- else
- popup.activeChild = null;
- }
- return;
- }
- },
-
- resetInput: function(me) {
- me.ignoreInputEventTimer = null;
- me.ignoreInputEvent = false;
- },
-
- processInput: function(me) {
- //Stop current lookup in case it's async.
- me.privatefunc.callListener(me, 'stopLookup');
- if (me.autoCompleteTimer) {
- clearTimeout(me.autoCompleteTimer);
- me.autoCompleteTimer = 0;
- }
-
- if (me.ignoreInputEvent)
- return;
-
- me.privatefunc.closePopupMenu(me);
- me.lastKeyPressed = 0;
-
- if (me.disableAutocomplete == "true")
- return;
-
- /*We want to autocomplete only if the user is editing at the end of the text */
- var inputElement = document.getAnonymousNodes(me)[0].firstChild;
- if (inputElement.selectionEnd >= inputElement.value.length)
- {
- me.needToAutocomplete = true;
- me.autoCompleteTimer = setTimeout(me.privatefunc.callListener, me.timeout, me, 'startLookup');
- }
- },
-
- processKeyPress: function(me, event) {
- if (me.disableAutocomplete == "true")
- {
- me.privatefunc.closePopupMenu(me);
- return;
- }
-
- var popup = document.getAnonymousNodes(me)[0].childNodes[1].firstChild;
- if (popup && me.menuOpen != "true")
- popup = null;
-
- me.lastKeyPressed = event.keyCode;
- switch (event.keyCode)
- {
- case 9: /*vk_tab*/
- if (popup)
- me.privatefunc.closePopupMenu(me);
- return;
-
- case 13 /*vk_return*/:
- if (me.autoCompleteTimer) {
- clearTimeout(me.autoCompleteTimer);
- me.autoCompleteTimer = 0;
- }
- me.privatefunc.closePopupMenu(me);
- me.privatefunc.finishAutoComplete(me, event);
- return;
-
- case 27 /*vk_escape*/:
- if (me.autoCompleteTimer) {
- clearTimeout(me.autoCompleteTimer);
- me.autoCompleteTimer = 0;
- }
- if (popup) {
- me.privatefunc.closePopupMenu(me);
- event.preventDefault();
- event.preventBubble();
- return;
- }
- break;
-
- case 37 /*vk_left*/:
- case 39 /*vk_right*/:
- if (me.autoCompleteTimer) {
- clearTimeout(me.autoCompleteTimer);
- me.autoCompleteTimer = 0;
- }
- if (popup)
- {
- me.privatefunc.closePopupMenu(me);
- event.preventDefault();
- event.preventBubble();
- }
- me.privatefunc.cleanupInputField(me);
- break;
-
- case 38 /*vk_up*/:
- case 40 /*vk_down*/:
- if (me.autoCompleteTimer) {
- clearTimeout(me.autoCompleteTimer);
- me.autoCompleteTimer = 0;
- }
- me.privatefunc.keyNavigation(me, event, popup);
- event.preventDefault();
- event.preventBubble();
- if (! popup)
- me.privatefunc.cleanupInputField(me);
- break;
- }
- },
-
- selectedItemIndex: 0
-
- })
- ]]>
- </property>
-
- </implementation>
-
- <handlers>
- <handler event="click" action="this.privatefunc.cleanupInputField(this);"/>
- <handler event="dblclick" action="this.privatefunc.cleanupInputField(this);"/>
- <handler event="input" action="this.privatefunc.processInput(this);"/>
- <handler event="keypress" action="this.privatefunc.processKeyPress(this, event);"/>
- <handler event="focus" action="this.needToAutocomplete = false; this.lastResults.searchString=''; this.ignoreInputEvent = false"/>
- <handler event="blur" action="
- this.privatefunc.closePopupMenu(this);
- if (this.needToAutocomplete)
- this.privatefunc.finishAutoComplete(this, event);
- this.privatefunc.cleanupInputField(this);
- "/>
- </handlers>
-
- </binding>
- </bindings>
-